/*
 * RT-Thread Secure
 *
 * Copyright (c) 2021, Shanghai Real-Thread Electronic Technology Co., Ltd.
 *
 * All rights reserved.
 */
#include "rthw.h"
#include "board.h"
#include "rtdevice.h"
#include "drv_gpio.h"
#include "rtdbg.h"
#include "rk3568_iomux.h"
#include "rk3568_gic.h"

#define GPIO_SWPORT_DR          0x00    /* Port Data Register */
#define GPIO_SWPORT_DDR         0x08    /* Port Data Direction Register */
#define GPIO_INT_EN             0x10    /* Interrupt Enable Register */
#define GPIO_INT_MASK           0x18    /* Interrupt Mask Register */
#define GPIO_INT_TYPE           0x20    /* Interrupt Level Register */
#define GPIO_INT_POLARITY       0x28    /* Interrupt Polarity Register */
#define GPIO_INT_BOTHEDGE       0x30    /* Interrupt Both Edge Type Register */
#define GPIO_DEBOUNCE           0x38    /* Debounce Enable Register */
#define GPIO_DBCLK_DIV_EN       0x40    /* DBCLK Divide Enable Register */
#define GPIO_INT_STATUS         0x50    /* Interrupt status of I/O Port. */
#define GPIO_PORT_EOI           0x60    /* Interrupt Clear Register */
#define GPIO_EXT_PORT           0x70    /* External Port Data Register */
#define GPIO_VER_ID             0x78    /* Version ID Register */

#define RK_GPIO_DIR_INPUT       0
#define RK_GPIO_DIR_OUTPUT      1

#define RK_GPIO_NORMAL          0
#define RK_GPIO_PULLUP          1
#define RK_GPIO_PULLDOWN        2

#define RK_GPIO_INT_LEVEL       0
#define RK_GPIO_INT_EDGE        1
#define RK_GPIO_INT_BOTHEDGE    1

#define RK_GPIO_INT_LOW         0
#define RK_GPIO_INT_HIGH        1

#define PIN_NUM_MAX             160

static rt_uint64_t gpio_base[] = 
{
    GPIO0_MMIO_BASE,//   0~31
    GPIO1_MMIO_BASE,//  32~63
    GPIO2_MMIO_BASE,//  64~95
    GPIO3_MMIO_BASE,// 96~127
    GPIO4_MMIO_BASE,//128~159
};

static rt_uint32_t gpio_int_id[] = 
{
    GPIO0_INI_ID,//   0~31
    GPIO1_INI_ID,//  32~63
    GPIO2_INI_ID,//  64~95
    GPIO3_INI_ID,// 96~127
    GPIO4_INI_ID,//128~159
};

rt_inline rt_base_t rk_to_num(gpio_port_t port, gpio_pin_t pin, gpio_index_t index)
{
    return port*100+pin*10+index;
}

rt_inline gpio_port_t num_to_rk_port(rt_base_t pin)
{
    return pin / 100;
}

rt_inline gpio_pin_t num_to_rk_pin(rt_base_t pin)
{
    return (pin % 100) / 10;
}

rt_inline gpio_index_t num_to_rk_index(rt_base_t pin)
{
    return (pin % 10) / 1;
}

static void rk_gpio_write(rt_base_t offset, rt_base_t pin, rt_base_t value)
{
    rt_uint32_t reg;
    rt_uint64_t addr;
    rt_uint32_t pin_offset;
    gpio_port_t port;
    gpio_pin_t p;
    gpio_index_t index;

    port = num_to_rk_port(pin);
    p = num_to_rk_pin(pin);
    index = num_to_rk_index(pin);

    if(p < 2)
    {
        pin_offset = 0x00;
    }
    else
    {
        pin_offset = 0x04;
        p -= 2;
    }
    addr = gpio_base[port] + pin_offset + offset;
    reg = readl(addr);

    if(value)
    {
        reg |= ((0x10000 | 0x01) << index) << (8 * p);
    }
    else
    {
        reg &= ~((0x01 << index) << (8 * p));
        reg |= (0x10000 << index) << (8 * p);
    }

    writel(reg, addr);
    writel(reg & 0x0000FFFF, addr);
}

static rt_uint32_t rk_gpio_read(rt_base_t offset, rt_base_t pin)
{
    rt_uint32_t reg;
    rt_uint64_t addr;
    rt_uint32_t pin_offset;
    rt_uint32_t value;
    gpio_port_t port;
    gpio_pin_t p;
    gpio_index_t index;

    port = num_to_rk_port(pin);
    p = num_to_rk_pin(pin);
    index = num_to_rk_index(pin);

    if(p < 2)
    {
        pin_offset = 0x00;
    }
    else
    {
        pin_offset = 0x04;
        p -= 2;
    }

    addr = gpio_base[port] + pin_offset + offset;

    reg = readl(addr);

    value = (reg & (1 << (8*p + index))) >> (8*p + index);

    return value;
}

static void rk_pin_write(rt_device_t dev, rt_base_t pin, rt_base_t value)
{
    rk_gpio_write(GPIO_SWPORT_DR, pin, value);
}

static int rk_pin_read(rt_device_t dev, rt_base_t pin)
{
    return rk_gpio_read(GPIO_EXT_PORT, pin);
}

static void rk_pin_mode(rt_device_t dev, rt_base_t pin, rt_base_t mode)
{
    gpio_port_t port;
    gpio_pin_t p;
    gpio_index_t index;

    port = num_to_rk_port(pin);
    p = num_to_rk_pin(pin);
    index = num_to_rk_index(pin);

    if(port > GPIO_PORT_NUM)
    {
        rt_kprintf("GPIO port error\n");
        return;
    }
    else if(p > GPIO_PIN_NUM)
    {
        rt_kprintf("GPIO pin error\n");
        return;
    }
    else if(index > GPIO_INDEX_NUM)
    {
        rt_kprintf("GPIO index error\n");
        return;
    }

    switch(mode)
    {
        case PIN_MODE_OUTPUT:
            iomux_set_mode(port, p, index, IOMUX_SEL_GPIO, GRF_PULL_MODE_Z,
                GRF_DRV_LEVEL_3, GRF_SPEED_LEVEL3, GRF_SMT_NO_HYSTERESIS,
                GRF_OD_MODE_DIS);
            rk_pin_write(dev, pin, 1);
            rk_gpio_write(GPIO_SWPORT_DDR, pin, RK_GPIO_DIR_OUTPUT);
            break;
        case PIN_MODE_INPUT:
            iomux_set_mode(port, p, index, IOMUX_SEL_GPIO, GRF_PULL_MODE_Z,
                GRF_DRV_LEVEL_3, GRF_SPEED_LEVEL3, GRF_SMT_NO_HYSTERESIS,
                GRF_OD_MODE_DIS);
            rk_gpio_write(GPIO_SWPORT_DDR, pin, RK_GPIO_DIR_INPUT);
            break;
        case PIN_MODE_INPUT_PULLUP:
            iomux_set_mode(port, p, index, IOMUX_SEL_GPIO, GRF_PULL_MODE_PULL_UP,
                GRF_DRV_LEVEL_3, GRF_SPEED_LEVEL3, GRF_SMT_NO_HYSTERESIS,
                GRF_OD_MODE_DIS);
            rk_gpio_write(GPIO_SWPORT_DDR, pin, RK_GPIO_DIR_INPUT);
            break;
        case PIN_MODE_INPUT_PULLDOWN:
            iomux_set_mode(port, p, index, IOMUX_SEL_GPIO, GRF_PULL_MODE_PULL_DOWN,
                GRF_DRV_LEVEL_3, GRF_SPEED_LEVEL3, GRF_SMT_NO_HYSTERESIS,
                GRF_OD_MODE_DIS);
            rk_gpio_write(GPIO_SWPORT_DDR, pin, RK_GPIO_DIR_INPUT);
            break;
        case PIN_MODE_OUTPUT_OD:
            iomux_set_mode(port, p, index, IOMUX_SEL_GPIO, GRF_PULL_MODE_Z,
                GRF_DRV_LEVEL_3, GRF_SPEED_LEVEL3, GRF_SMT_NO_HYSTERESIS,
                GRF_OD_MODE_EN);
            rk_gpio_write(GPIO_SWPORT_DDR, pin, RK_GPIO_DIR_OUTPUT);
    }
}

static void rk_pin_irq_state(rt_base_t pin, rt_uint32_t enabled)
{
    rk_gpio_write(GPIO_INT_EN, pin, enabled);
}

static struct rt_pin_irq_hdr pin_irq_hdr_tab = {0};

static void rk_hw_gpio_isr(int irq, void *param)
{
    rt_uint32_t rk_irq_stast = 0;
    rt_uint32_t irq_stast = 0;
    rt_uint8_t group_num = 0;
    rt_uint8_t i=0;

    group_num = irq - 65;
    rk_irq_stast = readl(gpio_base[group_num] + GPIO_INT_STATUS);
    irq_stast = rk_irq_stast;
    if(rk_irq_stast != 0)
    {
        for(i = 1; i < 33; i++)
        {
            irq_stast = irq_stast >> 1;
            if((irq_stast == 0x1) && (pin_irq_hdr_tab.hdr != RT_NULL))
            {
                rk_pin_irq_state((group_num * 100 + i), PIN_IRQ_DISABLE);
                
                pin_irq_hdr_tab.hdr(pin_irq_hdr_tab.args);

                /* clear irq */
                rk_gpio_write(GPIO_PORT_EOI, (group_num * 100 + i), 1);

                rk_pin_irq_state((group_num * 100 + i), PIN_IRQ_ENABLE);
            }
        }
    }
}

static rt_err_t rk_pin_attach_irq(struct rt_device *device, rt_int32_t pin,
    rt_uint32_t mode, void (*hdr)(void *args), void *args)
{
    gpio_port_t port;
    gpio_pin_t p;
    gpio_index_t index;
    
    port = num_to_rk_port(pin);
    p = num_to_rk_pin(pin);
    index = num_to_rk_index(pin);

    if(port > GPIO_PORT_NUM)
    {
        rt_kprintf("GPIO port error\n");
        return -RT_EINVAL;
    }
    else if(p > GPIO_PIN_NUM)
    {
        rt_kprintf("GPIO pin error\n");
        return -RT_EINVAL;
    }
    else if(index > GPIO_INDEX_NUM)
    {
        rt_kprintf("GPIO index error\n");
        return -RT_EINVAL;
    }

    if(device == RT_NULL)
    {
        rt_kprintf("Device NULL\n");
        return -RT_EINVAL;
    }

    iomux_set_iomux_sel(port, p, index, IOMUX_SEL_GPIO);

    pin_irq_hdr_tab.pin = pin;
    pin_irq_hdr_tab.hdr = hdr;
    pin_irq_hdr_tab.mode = mode;
    pin_irq_hdr_tab.args = args;

    rt_hw_interrupt_install(gpio_int_id[port], &rk_hw_gpio_isr, RT_NULL, "gpio");
    rt_hw_interrupt_mask(gpio_int_id[port]);
    /* trigger mode */
    switch(mode)
    {
    case PIN_IRQ_MODE_RISING:
        rk_gpio_write(GPIO_INT_TYPE, pin, RK_GPIO_INT_EDGE);
        rk_gpio_write(GPIO_INT_POLARITY, pin, RK_GPIO_INT_HIGH);
        break;
    case PIN_IRQ_MODE_FALLING:
        rk_gpio_write(GPIO_INT_TYPE, pin, RK_GPIO_INT_EDGE);
        rk_gpio_write(GPIO_INT_POLARITY, pin, RK_GPIO_INT_LOW);
        break;
    case PIN_IRQ_MODE_RISING_FALLING:
        rk_gpio_write(GPIO_INT_BOTHEDGE, pin, RK_GPIO_INT_BOTHEDGE);
        break;
    case PIN_IRQ_MODE_HIGH_LEVEL:
        rk_gpio_write(GPIO_INT_TYPE, pin, RK_GPIO_INT_LEVEL);
        rk_gpio_write(GPIO_INT_POLARITY, pin, RK_GPIO_INT_HIGH);
        break;
    case PIN_IRQ_MODE_LOW_LEVEL:
        rk_gpio_write(GPIO_INT_TYPE, pin, RK_GPIO_INT_LEVEL);
        rk_gpio_write(GPIO_INT_POLARITY, pin, RK_GPIO_INT_LOW);
        break;
    }

    return RT_EOK;
}

static rt_err_t rk_pin_dettach_irq(struct rt_device *device, rt_int32_t pin)
{
    gpio_port_t port;

    port = num_to_rk_port(pin);

    if(port > GPIO_PORT_NUM)
    {
        rt_kprintf("GPIO port error\n");
        return -RT_EINVAL;
    }

    if(device == RT_NULL)
    {
        rt_kprintf("Device NULL\n");
        return -RT_EINVAL;
    }

    pin_irq_hdr_tab.pin = -1;
    pin_irq_hdr_tab.mode = 0;
    pin_irq_hdr_tab.hdr = RT_NULL;
    pin_irq_hdr_tab.args = RT_NULL;

    rt_hw_interrupt_mask(gpio_int_id[port]);

    return RT_EOK;
}

static rt_err_t rk_pin_irq_enable(struct rt_device *device, rt_base_t pin,
    rt_uint32_t enabled)
{
    gpio_port_t port;

    port = num_to_rk_port(pin);

    if(port > GPIO_PORT_NUM)
    {
        rt_kprintf("GPIO port error\n");
        return -RT_EINVAL;
    }
    
    if(device == RT_NULL)
    {
        rt_kprintf("Device NULL\n");
        return -RT_EINVAL;
    }

    if(enabled == PIN_IRQ_ENABLE)
    {
        rt_kprintf("rk_pin_irq_enable\n");
        rt_hw_interrupt_umask(gpio_int_id[port]);
        rt_kprintf("(%d)\n", gpio_int_id[port]);
    }
    else if(enabled == PIN_IRQ_DISABLE)
    {
        rt_hw_interrupt_mask(gpio_int_id[port]);
    }

    rk_pin_irq_state(pin, enabled);

    return RT_EOK;
}

const static struct rt_pin_ops __pin_ops =
{
    rk_pin_mode,
    rk_pin_write,
    rk_pin_read,
    rk_pin_attach_irq,
    rk_pin_dettach_irq,
    rk_pin_irq_enable,
    RT_NULL,
};

int rk_hw_pin_init(void)
{
    rt_uint64_t g0_addr;
    rt_uint64_t g_addr;

    g0_addr = rt_ioremap_nocache(GPIO0_MMIO_BASE, 0x00010000);
    g_addr = rt_ioremap_nocache(GPIO1_MMIO_BASE, 0x00040000);

    gpio_base[0] = g0_addr;
    gpio_base[1] = g_addr;
    gpio_base[2] = g_addr + 0x00010000;
    gpio_base[3] = g_addr + 0x00020000;
    gpio_base[4] = g_addr + 0x00030000;

    return rt_device_pin_register("pin", &__pin_ops, RT_NULL);
}
INIT_BOARD_EXPORT(rk_hw_pin_init);

// static void rk_hw_gpio_isr1(int irq, void *param)
// {
//     rt_kprintf("rk_hw_gpio_isr1\n");
// }

// static void gpio_tets()
// {
//     rt_uint32_t gpio = GET_PIN(3, A, 6);
//     rt_pin_mode(gpio, PIN_MODE_INPUT);
//     // iomux_set_mode(3, 0, 6, IOMUX_SEL_GPIO, GRF_PULL_MODE_PULL_DOWN,
//     //             GRF_DRV_LEVEL_3, GRF_SPEED_LEVEL3, GRF_SMT_NO_HYSTERESIS,
//     //             GRF_OD_MODE_DIS);
//     rk_gpio_write(GPIO_SWPORT_DDR, gpio, RK_GPIO_DIR_INPUT);

//     rk_gpio_write(GPIO_INT_TYPE, gpio, RK_GPIO_INT_EDGE);
//     rk_gpio_write(GPIO_INT_POLARITY, gpio, RK_GPIO_INT_HIGH);

//     writel((0x10001 << 6), gpio_base[3] + 0x0010);
//     rt_kprintf("int enable : %p\n", readl(gpio_base[3] + 0x0010));

//     rt_hw_interrupt_install(gpio_int_id[3], &rk_hw_gpio_isr1, RT_NULL, "gpio");
//     rt_hw_interrupt_umask(gpio_int_id[3]);
//     rt_hw_interrupt_install(gpio_int_id[0], &rk_hw_gpio_isr1, RT_NULL, "gpio");
//     rt_hw_interrupt_umask(gpio_int_id[0]);
//     rt_hw_interrupt_install(gpio_int_id[1], &rk_hw_gpio_isr1, RT_NULL, "gpio");
//     rt_hw_interrupt_umask(gpio_int_id[1]);
//     rt_hw_interrupt_install(gpio_int_id[2], &rk_hw_gpio_isr1, RT_NULL, "gpio");
//     rt_hw_interrupt_umask(gpio_int_id[2]);
//     rt_hw_interrupt_install(gpio_int_id[4], &rk_hw_gpio_isr1, RT_NULL, "gpio");
//     rt_hw_interrupt_umask(gpio_int_id[4]);

//     while (1)
//     {
//         rt_kprintf("gpio : %d\n", rt_pin_read(gpio));
//         rt_thread_mdelay(1000);
//     }
    
// }
// MSH_CMD_EXPORT(gpio_tets, gpio_tets);
